/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package pgentity;

import db.DBContext;
import db.PGKeys;
import db.RedisKey;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import pgentity.pool.EntityFactory;
import pgentity.pool.EntityNonBlockingCache;
import pgentity.pool.EntityNonBlockingCache.Loader2;
import pgentity.pool.Identifier;
import share.PGError;
import share.PGException;
import share.PGHelper;

/**
 *
 * @author KieuAnh
 */
@EntityFactory(factorier = "loadMsg")
public class GameMessage implements PGEntity {
    private static final EntityNonBlockingCache<GameMessage> EntityCache;
    static
    {
        EntityCache = EntityNonBlockingCache.make2(GameMessage.class,
                50,
                1800,   // 30 mins
                new GMLoader());
    }
    
    private static class GMLoader implements Loader2<GameMessage>
    {
        @Override
        public GameMessage load(Identifier id) throws Exception {
            final String msgID = id.param(0, String.class);
            return loadMsg(msgID);
        }

        @Override
        public Map<Identifier, GameMessage> loadAll(List<Identifier> ids) throws Exception {            
            
            Map<String, Identifier> msgIDs = new HashMap(ids.size());
            for (Identifier id : ids) {
                msgIDs.put(id.param(0, String.class), id);
            }
            
            Map<String, GameMessage> msgs = loadMsgs(msgIDs.keySet());
            
            Map<Identifier, GameMessage> ret = new HashMap(ids.size());
            for (Map.Entry<String, GameMessage> msgEntry : msgs.entrySet()) {
                String msgID = msgEntry.getKey();
                GameMessage msg = msgEntry.getValue();
                
                ret.put(msgIDs.get(msgID), msg);
            }
            
            return ret;
        }
    }
    
    private final String msgID;
    private String content;
    private int order;
    private boolean enable = true;
    
    private GameMessage(String msgID) {
        this.msgID = msgID;
    }
    
    public static GameMessage getMsg(String msgID)
    {
        return EntityCache.get(msgID);
//        return EntityPool.inst().get(GameMessage.class, msgID);
    }
    
    public static Map<String, GameMessage> getMsgs(Collection<String> msgIDs)
    {
        Collection<Object[]> pars = new ArrayList(msgIDs.size());
        for (String msgID : msgIDs) {
            pars.add(new Object[] {msgID});
        }
        
        Map<Identifier, GameMessage> cacheGMs = EntityCache.get(pars);
        
        Map<String, GameMessage> msgs = new HashMap(msgIDs.size());
        for (Map.Entry<Identifier, GameMessage> msgEntry : cacheGMs.entrySet()) {
            String msgID = msgEntry.getKey().param(0, String.class);
            GameMessage msg = msgEntry.getValue();
            
            msgs.put(msgID, msg);
        }
        
        return msgs;
    }
    
    private static GameMessage loadMsg(String msgID)
    {
        PGException.Assert(DBContext.Redis().exists(redisKey(msgID)),
                PGError.UNDEFINED,
                "Game message: " + msgID + " not exist");
        
        GameMessage msg = new GameMessage(msgID);
        msg.updateFromDB();
        
        return msg;
    }
    
    private static Map<String, GameMessage> loadMsgs(Collection<String> msgIDs)
    {
        Map<RedisKey, String> msgKeys = new HashMap(msgIDs.size());
        for (String msgID : msgIDs) {
            msgKeys.put(redisKey(msgID), msgID);
        }
        
        Map<RedisKey, Boolean> checkExist = DBContext.Redis().mexists(msgKeys.keySet());
        for (Map.Entry<RedisKey, Boolean> existEntry : checkExist.entrySet()) {
            RedisKey key = existEntry.getKey();
            Boolean isExist = existEntry.getValue();
            
            if (!isExist)
            {
                throw new PGException(PGError.UNDEFINED,
                        "User " + msgKeys.get(key) + " doesn't exist");
            }
        }
        
        Map<RedisKey, Map<String, String>> msgsData = DBContext.Redis()
                .mhgetall(msgKeys.keySet());
        Map<String, GameMessage> msgs = new HashMap(msgIDs.size());
        for (Map.Entry<RedisKey, Map<String, String>> dataEntry : msgsData.entrySet()) {
            RedisKey key = dataEntry.getKey();
            Map<String, String> data = dataEntry.getValue();
            
            String msgID = msgKeys.get(key);
            GameMessage msg = new GameMessage(msgID);
            msg.fillData(data);
            
            msgs.put(msgID, msg);
        }
        
        return msgs;
    }
    
    public static GameMessage newMsg(String msgID,
            String content, int order, int expire)
    {
        GameMessage msg = new GameMessage(msgID);
        msg.content = content;
        msg.order = order;
        
        msg.saveToDB();
        DBContext.Redis().expire(redisKey(msgID), expire);
//        EntityPool.inst().put(msg, GameMessage.class, msgID);
        EntityCache.put(msg, msgID);
        
        return msg;
    }
    
    public static RedisKey redisKey(String msgID)
    {
        return PGKeys.GAMEMESSAGES.getChild(msgID);
    }
    
    public static void destroyMsg(String msgID)
    {
        DBContext.Redis().del(redisKey(msgID));
//        EntityPool.inst().remove(GameMessage.class, msgID);
        EntityCache.remove(msgID);
    }
    
    public static Map<String, Boolean> isExists(Collection<String> msgIDs)
    {
        List<RedisKey> msgKeys = new ArrayList(msgIDs.size());
        for (String msgID : msgIDs) {
            msgKeys.add(redisKey(msgID));
        }
        
        Map<RedisKey, Boolean> result = DBContext.Redis()
                .mexists(msgKeys);
        
        Map<String, Boolean> ret = new HashMap(msgIDs.size());
        for (String msgID : msgIDs) {
            ret.put(msgID, result.get(redisKey(msgID)));
        }
        
        return ret;
    }
    
    @Override
    public void updateFromDB()
    {
        Map<String, String> data = DBContext.Redis().hgetall(redisKey(msgID));
        fillData(data);
    }
    
    private void fillData(Map<String, String> data)
    {
        this.content = data.get("content");
        this.order = PGHelper.toInteger(data.get("order"));
        this.enable = PGHelper.toBoolean(data.get("enable"));
    }

    @Override
    public void saveToDB() {
        Map<String, String> data = new HashMap(2);
        
        data.put("content", content);
        data.put("order", String.valueOf(order));
        data.put("enable", String.valueOf(enable));
        
        DBContext.Redis().hset(redisKey(msgID), data);
    }

    public String getMsgID() {
        return msgID;
    }

    public String getContent() {
        return content;
    }

    public int getOrder() {
        return order;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    public boolean isEnable() {
        return enable;
    }

    public void setEnable(boolean enable) {
        this.enable = enable;
    }

    public Map<String, Object> buildAMF()
    {
        Map<String, Object> data = new HashMap();
        
        data.put("msgID", this.msgID);
        data.put("content", content);
        data.put("order", this.order);
        
        return data;
    }
}
